package org.yray.netty.lengthfield;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.bytes.ByteArrayEncoder;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.CharsetUtil;
import io.netty.util.HashedWheelTimer;
import io.netty.util.concurrent.ScheduledFuture;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.yray.netty.demo.NettyClientHandler;

import java.util.concurrent.TimeUnit;

@Slf4j
public class TcpClient implements Runnable {

    private NettyLengthFieldClientHandler nettyLengthFieldClientHandler;

    private Bootstrap bootstrap;
    private EventLoopGroup group;

    private String host="127.0.0.1";
    private int port=9000;
    HashedWheelTimer hashedWheelTimer=new HashedWheelTimer();

    public TcpClient(NettyLengthFieldClientHandler nettyLengthFieldClientHandler)
    {
        nettyLengthFieldClientHandler.setTcpClient(this);
        this.nettyLengthFieldClientHandler=nettyLengthFieldClientHandler;

    }

    @Override
    public void run() {
        //客户端需要一个事件循环组
        group = new NioEventLoopGroup();
        try {
            //创建客户端启动对象
            //注意客户端使用的不是 ServerBootstrap 而是 Bootstrap
            bootstrap = new Bootstrap();
            //设置相关参数
            bootstrap.group (group) //设置线程组
                    .option(ChannelOption.TCP_NODELAY,true)
                    .option(ChannelOption.SO_KEEPALIVE, true)
                    .channel (NioSocketChannel.class) // 使用 NioSocketChannel 作为客户端的通道实现
                    .handler (new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel channel) throws Exception {
                            channel.pipeline().addLast(new IdleStateHandler(0, 30, 0, TimeUnit.SECONDS));
                            //规定标记消息提长度所占字节数
                            channel.pipeline().addLast(new LengthFieldBasedFrameDecoder(1024, 0, 2, 0, 2));
                            channel.pipeline().addLast(new LengthFieldPrepender(2));
                            channel.pipeline().addLast("encode",new ByteArrayEncoder());
                            channel.pipeline ().addLast (nettyLengthFieldClientHandler);
                        }
                    });
            connet();
        } catch (Exception e) {
            System.out.println("wait..");
        } finally {

            System.out.println("finally..");
            hashedWheelTimer.newTimeout((t)->connet(),5,TimeUnit.SECONDS);
        }
    }

    public void connet()  {
        try {
        //启动客户端去连接服务器端
        ChannelFuture future = bootstrap.connect(host,port).sync();
        //客户端断线重连逻辑
        future.addListener((ChannelFutureListener) future1 -> {
            if (future1.isSuccess()) {
                System.out.println("连接MediaServer成功");

            } else {
                System.out.println("连接失败，进行断线重连,runIp={},runPort={}");
                future1.channel().eventLoop().schedule(() -> connectAsync(), 20, TimeUnit.SECONDS);
            }
        });
        nettyLengthFieldClientHandler.setChannel(future.channel());
        //future.channel().closeFuture().sync();
        future.channel().closeFuture().addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                System.out.println("close");
                //group.shutdownGracefully ();
            }
        }).sync();
        } catch (Exception e) {
            System.out.println("error="+ ExceptionUtils.getStackTrace(e));
        }
    }

    public void connectAsync() {
        System.out.println("尝试连接到服务端: {}:{}"+host+port);
        try {
            ChannelFuture channelFuture = bootstrap.connect(host, port).sync();
            channelFuture.addListener((ChannelFutureListener) future -> {
                if (!future.isSuccess()) {
                    System.out.println("等待下一次重连");
                    hashedWheelTimer.newTimeout((t)->{
                        connectAsync();
                    },5,TimeUnit.SECONDS);

                } else {
                    nettyLengthFieldClientHandler.setChannel( future.channel());
                    System.out.println("服务重连成功!!! {} connect to server"+future.channel().localAddress());
                }
            });
            channelFuture.channel().closeFuture().sync();
        }catch (Exception e){
            System.out.println("重连出错：{}"+ExceptionUtils.getStackTrace(e));
            hashedWheelTimer.newTimeout((t)->{
                connectAsync();
            },1,TimeUnit.SECONDS);
        }

    }
}
